/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "socket.h"

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <io.h>
#else
#include <sys/stat.h>
#include <sys/socket.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#endif

#include <cstring>
#include <cassert>
#include <event2/event.h>
#include "node/assert.h"
#include "node/util/string.h"
#include "node/system/error.h"
#include "node/system/console.h"
#include "node/net/tcp_server.h"
using namespace ::node::util;
using namespace ::node::system;

namespace node {
    namespace net {

        Socket::Socket(void)
            : id_(-1)
            , fd_(-1)
            , bev_(NULL)
            , host_("")
            , port_(0)
            , error_string_("")
            , errno_(0)
            , fatal_error_(false)
            , owner_(NULL)
        {

        }

        Socket::~Socket(void)
        {
            doClose();
        }

        bool Socket::open(struct event_base *ev_base, ::evutil_socket_t fd,
        struct sockaddr *sa /* = NULL */, int socklen /* = 0 */)
        {
            assert(ev_base && fd != -1);
            fd_ = fd;

            do
            {
                {
                    // parse address
                    struct sockaddr_storage ss;
                    ev_socklen_t socklen = sizeof(ss);
                    char addrbuf[128];
                    void *inaddr;
                    memset(&ss, 0, sizeof(ss));
                    if (getpeername(fd, (struct sockaddr *)&ss, &socklen)) {
                        console::error("getsockname() failed");
                        break;
                    }
                    if (ss.ss_family == AF_INET) {
                        port_ = ntohs(((struct sockaddr_in*)&ss)->sin_port);
                        inaddr = &((struct sockaddr_in*)&ss)->sin_addr;
                    }
                    else if (ss.ss_family == AF_INET6) {
                        port_ = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
                        inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
                    }
                    else {
                        console::error("Weird address family %d\n",
                            ss.ss_family);
                        return 1;
                    }
                    host_ = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
                        sizeof(addrbuf));
                }

                bev_ = bufferevent_socket_new(ev_base, fd, BEV_OPT_CLOSE_ON_FREE);
                if (!bev_) {
                    break;
                }

                bufferevent_setcb(bev_,
                    &Socket::readCallback, 
                    &Socket::writeCallback, 
                    &Socket::eventCallback, 
                    this);
                bufferevent_disable(bev_, EV_WRITE);
                bufferevent_enable(bev_, EV_READ);

                return true;
            } while (false);

            return false;
        }

        void Socket::close(void)
        {
            NODE_ASSERT(owner_ != NULL);
            owner_->closeSocket(this);
        }

        void Socket::send(const ::std::uint8_t *data, ::std::size_t size)
        {
            if (!fatal_error_)
            {
                bufferevent_enable(bev_, EV_WRITE);
                if (bufferevent_write(bev_, data, size) != 0) {
                    eofOrError();
                }
            }
        }

        void Socket::enableRead(bool enable) {
            if (enable) {
                bufferevent_enable(bev_, EV_READ);
            } else {
                bufferevent_disable(bev_, EV_READ);
            }
        }

        void Socket::enableWrite(bool enable) {
            if (enable) {
                bufferevent_enable(bev_, EV_WRITE);
            } else {
                bufferevent_disable(bev_, EV_WRITE);
            }
        }

        void Socket::setTimeout(::time_t read_timeout, ::time_t write_timeout /* = -1 */)
        {
            struct timeval tv_read, tv_write;
            if (read_timeout != (::time_t)-1) {
                tv_read.tv_sec = long(read_timeout / 1000);
                tv_read.tv_usec = long((read_timeout % 1000) * 1000);
            }
            if (write_timeout != (::time_t)-1) {
                tv_write.tv_sec = long(write_timeout / 1000);
                tv_write.tv_usec = long((write_timeout % 1000) * 1000);
            }

            if (read_timeout != (::time_t)-1 && write_timeout != (::time_t)-1) {
                bufferevent_set_timeouts(bev_, &tv_read, &tv_write);
            } else if (read_timeout != (::time_t)-1) {
                bufferevent_set_timeouts(bev_, &tv_read, NULL);
            } else {
                bufferevent_set_timeouts(bev_, NULL, &tv_write);
            }
        }

        void Socket::doClose(void)
        {
            if (bev_)
            {
                bufferevent_disable(bev_, EV_READ);
                bufferevent_free(bev_);
                bev_ = NULL;
                fd_ = -1;
            }
        }

        void Socket::eofOrError(void)
        {
            if (fatal_error_) {
                return;
            }
            fatal_error_ = true;

            bufferevent_disable(bev_, EV_WRITE);
        }

        void Socket::readCallback(struct bufferevent *, void *user_data)
        {
            Socket *psock = static_cast<Socket*>(user_data);
            assert(psock);
            psock->onRead();
        }

        void Socket::writeCallback(struct bufferevent *, void *user_data)
        {
            Socket *psock = static_cast<Socket*>(user_data);
            assert(psock);

            if (psock->fatal_error_)
            {
                bufferevent_disable(psock->bev_, EV_WRITE);
                psock->onWrite();
            }
        }

        void Socket::eventCallback(struct bufferevent *, short events, void *user_data)
        {
            Socket *psock = static_cast<Socket*>(user_data);
            assert(psock);

            if (events & BEV_EVENT_EOF) {
                psock->eofOrError();
                psock->onEof();
            }
            else if (events & BEV_EVENT_ERROR) {
                psock->errno_ = error::neterrcode();
                error::errstring(psock->error_string_, psock->errno_);
                psock->eofOrError();
                psock->onError();
            }
            else if (events & BEV_EVENT_TIMEOUT) {
                int flags = 0;
                if (events & BEV_EVENT_READING) {
                    flags |= BEV_EVENT_READING;
                }
                if (events & BEV_EVENT_WRITING) {
                    flags |= BEV_EVENT_WRITING;
                }
                psock->onTimeout(flags);
            }
        }

    }
}